EDA

Trabajo practico SMN

Marco Di Sario , Lucio Holvoet, Santino Silva

Librerias

library(tidyverse) 
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(ggplot2) 
library(dplyr) 
library(DataExplorer) 
library(inspectdf) 
library(lubridate) 
library(forecast) 
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
library(gridExtra) 

Adjuntando el paquete: 'gridExtra'

The following object is masked from 'package:dplyr':

    combine
library(plotly) 

Adjuntando el paquete: 'plotly'

The following object is masked from 'package:ggplot2':

    last_plot

The following object is masked from 'package:stats':

    filter

The following object is masked from 'package:graphics':

    layout
library(skimr) 
library(stringr) 
library(stringi) 
library(ggspatial)
library(leaflet)
library(raster)
Cargando paquete requerido: sp

Adjuntando el paquete: 'raster'

The following object is masked from 'package:skimr':

    bind

The following object is masked from 'package:plotly':

    select

The following object is masked from 'package:dplyr':

    select
library(lubridate)
library(sf)
Linking to GEOS 3.12.1, GDAL 3.8.4, PROJ 9.3.1; sf_use_s2() is TRUE
library(RColorBrewer)
library(shiny)
library(prophet) 
Cargando paquete requerido: Rcpp
Cargando paquete requerido: rlang

Adjuntando el paquete: 'rlang'

The following objects are masked from 'package:purrr':

    %@%, flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl,
    flatten_raw, invoke, splice
library(forcats)
# current_working_dir = dirname(rstudioapi::getActiveDocumentContext()$path)
# setwd(current_working_dir)
# getwd()

Carga de datos

raw_estaciones <- read.csv(
  file = "smn_estaciones.csv"
  )
raw_smn_precipitaciones <- read.csv(
  file = "smn_precipitaciones-1991-2024.txt"
  )

Juntamos y guardamos en un solo dataset todos los registros diarios del clima

# directorio_datos = "C:\\Users\\disar\\OneDrive\\Escritorio\\facultad python\\programacion_2_pablo\\TP2\\smn-data"
# # Nombres de columnas
# nombres_columnas = c("FECHA", "HORA", "TEMP", "HUM", "PNM", "DD", "FF", "NOMBRE")
# # Anchos de las columnas
# anchos_columnas <- c(8, 6, 6, 6, 7, 6, 8, 53)
# # Clases (tipos) de las columnas
# # Forzar FECHA a tipo character, para evitar que lo convierta a numérico y el primer "0" se borre
# clases_columnas <- c("character", "numeric", "numeric", "numeric", "numeric", "numeric", "character", "character")
# 
# # Listar todos los archivos que siguen el patrón "datohorario*.txt"
# archivos <- list.files(path = directorio_datos, pattern = "datohorario.*\\.txt", full.names = TRUE)
# 
# # Crear un objeto de progreso
# pb <- progress_bar$new(
#   format = "Procesando :current/:total [:bar] :percent en :elapsedfull (:tick_rate/s - ETA: :eta)",
#   total = length(archivos),
#   clear = FALSE,
#   width = 80
# )
# 
# # Función para leer cada archivo
# leer_archivo <- function(archivo) {
#   pb$tick()  # Avanza la barra de progreso
#   
#   datos <- read.fwf(archivo, 
#                     col.names = nombres_columnas,
#                     widths = anchos_columnas, 
#                     colClasses = clases_columnas,
#                     fileEncoding = "ISO-8859-1", 
#                     skip = 2,    # saltear dos primeras líneas
#                     strip.white = TRUE,  # borrar espacios en campos char
#                     dec = ".")
#   
#   # Borrar filas con FECHA vacía o NA
#   datos <- datos |> filter(FECHA != "" & !is.na(FECHA))
#   
#   # Convertir la columna FECHA a formato Date con el patrón ddmmYYYY
#   datos$FECHA <- as.Date(datos$FECHA, format = "%d%m%Y")
#   
#   # Convertir FF a numérico
#   datos$FF <- as.numeric(datos$FF)
#   
#   return(datos)
# }
# 
# # Leer y combinar todos los archivos en un solo dataframe
# df <- do.call(bind_rows, lapply(archivos, leer_archivo))
# 
# # En vez de guardar un archivo csv, usar formato RDS, más abajo
# # Guardar el dataframe df en un archivo CSV
# # write.csv(df, "C://clean_msn//datos_smn.csv", row.names = FALSE)
# 
# # Guardar el dataframe en formato RDS con compresión
# saveRDS(df, file = "C:\\Users\\disar\\OneDrive\\Escritorio\\facultad python\\programacion_2_pablo\\TP2\\datos_smn.rds")
### Cargamos el dataset

df_smn <- readRDS('datos_smn.rds')

Diccionario

Dataset Desc
smn_estaciones.csv con datos de las estaciones meteorológicas de Argentina
smn_precipitaciones-1991-2024 con datos de precipitaciones
datohorario20230102.txt

Los datos diarios

n FECHA, HORA [HOA], TEMP [ºC], HUM [%], PNM [hPa], DD [gr], FF [km/hr],

Indicaciones

  1. Combinar los datos provistos en un único dataset que contenga todos los datos relevantes

  2. - Importar los datos y limpiarlos usando técnicas aprendidas en clase (dplyr, tidyr).

  3. - Realizar un análisis exploratorio completo, incluyendo estadísticas descriptivas y visualizaciones con ggplot2 o plotly. Realizar gráficos georeferenciados interactivos.

  4. - Identificar y comunicar los hallazgos más importantes del dataset.

  5. - Identificar y manejar valores faltantes y duplicados.

  6. - Transformar variables según sea necesario (por ejemplo, convertir fechas a un formato adecuado, categorizar variables, etc.).

  7. graficar

Analisis de Datasets por separado

smn_estaciones.csv

123 observaciones 6 variables

raw_estaciones <- raw_estaciones %>%
  rename(id_estacion = Nro) %>%
  rename_with(tolower)
plot_intro(raw_estaciones)

raw_estaciones %>%
  inspect_cat() %>%    
  show_plot()

glimpse(raw_estaciones)
Rows: 123
Columns: 6
$ nombre      <chr> "AEROPARQUE AERO", "AZUL AERO", "BAHIA BLANCA AERO", "BARI…
$ provincia   <chr> "CAPITAL FEDERAL", "BUENOS AIRES", "BUENOS AIRES", "RIO NE…
$ altura      <int> 6, 147, 83, 835, 256, 11, 24, 198, 12, 7, 207, 815, 94, 25…
$ id_estacion <int> 87582, 87641, 87750, 87765, 89034, 89053, 88963, 89055, 88…
$ latitud     <dbl> -34.56667, -36.83333, -38.71667, -41.15000, -77.86667, -62…
$ longitud    <dbl> -58.41667, -59.88333, -62.16667, -71.16667, -34.63333, -58…

smn_precipitaciones.csv

1.362.054 filas y 3 observaciones

Fecha tiene formato caracter

Precipitacion MM tiene formato caracter

raw_smn_precipitaciones <- raw_smn_precipitaciones %>%
  rename(
    id_estacion = Estacion, precipitacion = Precipitacion..mm.
    ) %>%
  rename_with(tolower)
plot_intro(raw_smn_precipitaciones)

raw_smn_precipitaciones %>%
  inspect_cat() %>%    # library(inspectdf)
  show_plot()

glimpse(raw_smn_precipitaciones)
Rows: 1,362,054
Columns: 3
$ id_estacion   <int> 87007, 87007, 87007, 87007, 87007, 87007, 87007, 87007, …
$ fecha         <chr> "1991-01-01", "1991-01-02", "1991-01-03", "1991-01-04", …
$ precipitacion <chr> "5.2", "8", "0", "0", "0", "29.4", "25.5", "6", "3", "16…
# Obtener un resumen completo del dataframe
skim(raw_smn_precipitaciones)
Data summary
Name raw_smn_precipitaciones
Number of rows 1362054
Number of columns 3
_______________________
Column type frequency:
Date 1
numeric 2
________________________
Group variables None

Variable type: Date

skim_variable n_missing complete_rate min max median n_unique
fecha 0 1 1991-01-01 2024-09-20 2008-02-24 12317

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
id_estacion 0 1.00 87481.73 256.09 87007 87281 87497 87679 87938.0 ▆▆▇▇▆
precipitacion 63906 0.95 2.17 8.39 0 0 0 0 302.7 ▇▁▁▁▁
raw_smn_precipitaciones <- raw_smn_precipitaciones %>% mutate(año = year(fecha),
         mes = month(fecha),
         dia = day(fecha))

Datos_smn.rds

### Renombramos las columnas para que sean todas iguales


df_smn <- df_smn %>% rename(
  fecha = FECHA,
  hora = HORA,
  temperatura = TEMP,
  humedad = HUM,
  presion_atmosferica = PNM,
  direccion_viento = DD,
  velocidad_viento = FF,
  nombre = NOMBRE
)

Pasar columnas a valores correspondientes y crear columnas utiles

colnames(df_smn)
[1] "fecha"               "hora"                "temperatura"        
[4] "humedad"             "presion_atmosferica" "direccion_viento"   
[7] "velocidad_viento"    "nombre"             
# df_smn$fecha <- as.Date(df_smn$fecha_texto, format = "%Y-%m-%d")

# raw_smn_precipitaciones$fecha <- as.Date(raw_smn_precipitaciones$fecha_texto, format = "%Y-%m-%d")
class(df_smn$fecha)
[1] "Date"
class(raw_smn_precipitaciones$fecha)
[1] "Date"

Vista superficial

plot_intro(df_smn)

Unimos todos los datasets a traves de joins para poder trabajar de forma mas comoda

colnames(df_smn)
[1] "fecha"               "hora"                "temperatura"        
[4] "humedad"             "presion_atmosferica" "direccion_viento"   
[7] "velocidad_viento"    "nombre"             
colnames(raw_estaciones)
[1] "nombre"      "provincia"   "altura"      "id_estacion" "latitud"    
[6] "longitud"   
colnames(raw_smn_precipitaciones)
[1] "id_estacion"   "fecha"         "precipitacion" "año"          
[5] "mes"           "dia"          
df_precip_estaciones = left_join(raw_smn_precipitaciones, raw_estaciones, by = 'id_estacion')

# Convertir la columna Fecha en df_combinado a tipo Date
df_precip_estaciones$fecha = as.Date(df_precip_estaciones$fecha, format = "%Y-%m-%d")

# Unir el dataframe combinado con el tercer dataset


df_completo = inner_join(df_precip_estaciones, df_smn, by = c("nombre", "fecha"))

# write.csv(df_completo, "C:\\Users\\disar\\OneDrive\\Escritorio\\facultad python\\programacion_2_pablo\\TP2\\df_completo.csv", row.names = FALSE)
# df_completo <- read.csv('df_completo.csv')

df_completo$fecha <- as.Date(df_completo$fecha)

df_completo <- df_completo %>% mutate(provincia = str_to_lower(provincia)) 

Analisis exploratorio

Luego de unir los datasets de estaciones, precipitaciones y datos climáticos, contamos con una base consolidada que nos permitirá analizar cómodamente las variables meteorológicas y su relación con la ubicación y el tiempo. Este análisis exploratorio inicial nos ayudará a entender mejor los datos, identificar patrones y destacar posibles irregularidades, preparando el camino para un estudio más detallado del clima en distintas regiones de Argentina.

Breve analisis superficial de las columnas, buscando NAs o valores atipicos

head(df_completo)
  id_estacion      fecha precipitacion  año mes dia                 nombre
1       87007 2019-10-22             0 2019  10  22 LA QUIACA OBSERVATORIO
2       87007 2019-10-22             0 2019  10  22 LA QUIACA OBSERVATORIO
3       87007 2019-10-22             0 2019  10  22 LA QUIACA OBSERVATORIO
4       87007 2019-10-22             0 2019  10  22 LA QUIACA OBSERVATORIO
5       87007 2019-10-22             0 2019  10  22 LA QUIACA OBSERVATORIO
6       87007 2019-10-22             0 2019  10  22 LA QUIACA OBSERVATORIO
  provincia altura latitud longitud hora temperatura humedad
1     jujuy   3459   -22.1    -65.6    0         8.2      61
2     jujuy   3459   -22.1    -65.6    1         7.0      64
3     jujuy   3459   -22.1    -65.6    2         6.1      66
4     jujuy   3459   -22.1    -65.6    3         4.8      72
5     jujuy   3459   -22.1    -65.6    4         3.7      73
6     jujuy   3459   -22.1    -65.6    5         2.8      75
  presion_atmosferica direccion_viento velocidad_viento
1                3112              360                9
2                3111              360                9
3                3108              360                9
4                3105                0                0
5                3101                0                0
6                3098                0                0
summary(df_completo)
  id_estacion        fecha            precipitacion          año      
 Min.   :87007   Min.   :2018-01-01   Min.   :  0.000   Min.   :2018  
 1st Qu.:87305   1st Qu.:2019-10-02   1st Qu.:  0.000   1st Qu.:2019  
 Median :87497   Median :2021-06-13   Median :  0.000   Median :2021  
 Mean   :87483   Mean   :2021-05-30   Mean   :  1.987   Mean   :2021  
 3rd Qu.:87648   3rd Qu.:2023-01-31   3rd Qu.:  0.000   3rd Qu.:2023  
 Max.   :87938   Max.   :2024-09-20   Max.   :276.000   Max.   :2024  
                                      NA's   :10699                   
      mes              dia           nombre           provincia        
 Min.   : 1.000   Min.   : 1.00   Length:4596655     Length:4596655    
 1st Qu.: 3.000   1st Qu.: 8.00   Class :character   Class :character  
 Median : 6.000   Median :16.00   Mode  :character   Mode  :character  
 Mean   : 6.364   Mean   :15.69                                        
 3rd Qu.: 9.000   3rd Qu.:23.00                                        
 Max.   :12.000   Max.   :31.00                                        
                                                                       
     altura          latitud          longitud           hora      
 Min.   :   5.0   Min.   :-54.83   Min.   :-72.05   Min.   : 0.00  
 1st Qu.:  46.0   1st Qu.:-37.23   1st Qu.:-66.35   1st Qu.: 7.00  
 Median : 130.0   Median :-33.72   Median :-63.77   Median :12.00  
 Mean   : 316.8   Mean   :-34.56   Mean   :-63.22   Mean   :12.08  
 3rd Qu.: 461.0   3rd Qu.:-30.23   3rd Qu.:-59.05   3rd Qu.:18.00  
 Max.   :3459.0   Max.   :-22.10   Max.   :-53.67   Max.   :23.00  
                                                                   
  temperatura        humedad       presion_atmosferica direccion_viento
 Min.   :-19.80   Min.   :  0.00   Min.   :   9.3      Min.   :  0.0   
 1st Qu.: 10.80   1st Qu.: 46.00   1st Qu.:1009.3      1st Qu.: 70.0   
 Median : 17.10   Median : 65.00   Median :1014.4      Median :180.0   
 Mean   : 16.91   Mean   : 63.85   Mean   :1067.7      Mean   :176.4   
 3rd Qu.: 23.00   3rd Qu.: 83.00   3rd Qu.:1020.4      3rd Qu.:270.0   
 Max.   : 45.60   Max.   :100.00   Max.   :8500.0      Max.   :990.0   
 NA's   :1223     NA's   :3762     NA's   :88110       NA's   :1569    
 velocidad_viento
 Min.   :  0.00  
 1st Qu.:  6.00  
 Median : 11.00  
 Mean   : 13.24  
 3rd Qu.: 19.00  
 Max.   :133.00  
 NA's   :1577    

Muchas columnas, dificil de leer aunque sea informacion clara

NAs de cada columna

                                Columna Cantidad_NA
id_estacion                 id_estacion           0
fecha                             fecha           0
precipitacion             precipitacion       10699
año                                 año           0
mes                                 mes           0
dia                                 dia           0
nombre                           nombre           0
provincia                     provincia           0
altura                           altura           0
latitud                         latitud           0
longitud                       longitud           0
hora                               hora           0
temperatura                 temperatura        1223
humedad                         humedad        3762
presion_atmosferica presion_atmosferica       88110
direccion_viento       direccion_viento        1569
velocidad_viento       velocidad_viento        1577

Dado que el dataset tiene 4.5 millones de observaciones, los 88,110 valores nulos en la columna de presión atmosférica representan menos del 2% del total. Este porcentaje de datos faltantes es relativamente bajo en comparación con el volumen total, por lo que no debería afectar significativamente el análisis de esta variable. Sin embargo, es recomendable evaluar si los valores nulos están distribuidos aleatoriamente o concentrados en ciertos períodos o estaciones, lo cual podría influir en la interpretación.

Ahora chequeamos si esa columna tiene valores atipicos

detectar_valores_atipicos <- function(df, columna_presion = "presion_atmosferica", max_presion = 1040, min_presion = 950) {
  # Filtrar filas con valores atípicos
  valores_atipicos <- df %>% 
    filter(!!sym(columna_presion) > max_presion | !!sym(columna_presion) < min_presion)
  
  # Contar valores atípicos
  cantidad_atipicos <- nrow(valores_atipicos)
  
  # Imprimir el número de valores atípicos y los límites de presión
  print(paste("Cantidad de valores atípicos:", cantidad_atipicos))
  print(paste("Valor máximo de presión registrado en Argentina:", max_presion))
  print(paste("Valor mínimo de presión registrado en Argentina:", min_presion))
  
  # Guardar los valores atípicos en un nuevo dataframe
  return(valores_atipicos)
}

# Ejemplo de uso
valores_atipicos_presion <- detectar_valores_atipicos(df_completo, "presion_atmosferica")
[1] "Cantidad de valores atípicos: 392146"
[1] "Valor máximo de presión registrado en Argentina: 1040"
[1] "Valor mínimo de presión registrado en Argentina: 950"

Guardar el dataset de valores atípicos en un archivo CSV (opcional)

# write.csv(valores_atipicos_presion, "valores_atipicos_presion.csv", row.names = FALSE)

Cargamos el dataset y observamos

# valores_atipicos <- read.csv("valores_atipicos_presion.csv")

Viendo el dataset de los datos erroneos de, en 2019, se identificaron valores atípicos en los datos de la estación La Quiaca Observatorio, lo que sugiere problemas específicos en la recolección de información en esa ubicación. La concentración de errores en un solo punto resalta la importancia de investigar la causa raíz, ya que estos datos erróneos pueden distorsionar análisis climáticos y conducir a conclusiones incorrectas.

Ya que son al rededor de 400.000 de datos erroneos sumando los NAs podemos concluir que por ahi no es adecuado trabajar con esta columna, no eliminaremos las filas ya que son un monton, pero tampoco la utilizaremos.

Matriz de correlacion

Chequeamos la relacion de algunas variables para chequear que todo tenga sentido y el analisis valga la pena

library(ggcorrplot)
library(corrplot)


#Seleccionar solo las variables especificadas
df_seleccion <- df_completo %>%
    dplyr::select(precipitacion, altura, hora, humedad, temperatura, velocidad_viento)

#Calcular la matriz de correlación
matriz_correlacion <- cor(df_seleccion, use = "complete.obs")

#Graficar la matriz de correlación
corrplot(matriz_correlacion, method = "circle", type = "upper",
         col = colorRampPalette(c("blue", "white", "red"))(200), 
         tl.col = "black", tl.srt = 45,
         title = "Matriz de Correlación entre Variables Seleccionadas")

Todo parece estar en orden, pasamos a un analisis más especifico

Ahora si pasamos a un analisis un poco mas profundo

# Cargar el archivo GeoJSON de las provincias
argentina <- st_read("provincia.JSON")
Reading layer `provincia' from data source 
  `C:\Users\disar\OneDrive\Escritorio\facultad python\programacion_2_pablo\TP2\provincia.json' 
  using driver `GeoJSON'
Simple feature collection with 24 features and 8 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -74 ymin: -90 xmax: -25 ymax: -21.78086
Geodetic CRS:  WGS 84
argentina <- argentina %>%
  mutate(provincia = stri_trans_general(nam, "Latin-ASCII"))

argentina <- argentina %>% mutate(provincia = str_to_lower(provincia)) 

Numero de estaciones por provincia

estaciones_por_provincia <- df_completo %>%
  group_by(provincia) %>%
  summarise(num_estaciones = n_distinct(id_estacion))

ggplot(estaciones_por_provincia, aes(x = reorder(provincia, -num_estaciones), y = num_estaciones)) +
  geom_bar(stat = "identity", fill = "steelblue") +
  coord_flip() +
  labs(title = "Número de Estaciones Meteorológicas por Provincia",
       x = "Provincia",
       y = "Número de Estaciones")

Podemos observar una gran cantidad de estaciones meteorologicas en la provincia de buenos aires.

estaciones <- df_completo %>%
  distinct(id_estacion, .keep_all = TRUE) %>%
  dplyr::select(id_estacion, nombre, provincia, latitud, longitud)

mapa_ggplot <- ggplot() +
  geom_sf(data = argentina, fill = "lightgray", color = "black") + 
  geom_point(data = estaciones, aes(x = longitud, y = latitud, color = provincia, text = nombre), 
             size = 3, alpha = 0.7) +
  labs(
    title = "Mapa de Estaciones Meteorológicas Únicas en Argentina",
    x = "Longitud",
    y = "Latitud"
  ) +
  scale_color_viridis_d(name = "Provincia") +  
  theme_minimal()


mapa_interactivo <- ggplotly(mapa_ggplot, tooltip = "text")  


mapa_interactivo

vemos las estaciones situadas en el mapa .

Calculamos las top 10 provincias con mayor humedad media

top_humedad <- df_completo %>%
  group_by(provincia) %>%
  summarize(humedad_media = mean(humedad, na.rm = TRUE)) %>%
  arrange(desc(humedad_media)) %>%
  slice_head(n = 10) # Selecciona las 10 primeras

# Graficar el top 10 de provincias con mayor humedad media
ggplot(top_humedad, aes(x = reorder(provincia, humedad_media), y = humedad_media)) +
  geom_bar(stat = "identity", fill = "skyblue") +
  coord_flip() +
  labs(title = "Top 10 Provincias con Mayor Humedad Media",
       x = "Provincia", y = "Humedad Media (%)") +
  theme_minimal()

Observamos que Misiones presenta la mayor cantidad de precipitación, seguida de Corrientes y Chaco, lo cual refleja el clima húmedo del noreste argentino. En contraste, San Juan y Santa Cruz muestran las menores precipitaciones, lo que es consistente con sus climas más secos y áridos. Este gráfico destaca la variabilidad climática regional en Argentina, con una clara distinción entre las provincias del noreste y las zonas más áridas del oeste y sur del país.

Hacemos lo mismo pero para las precipitaciones

top_precipitacion <- df_completo %>%
  group_by(provincia) %>%
  summarize(precipitacion_media = mean(precipitacion, na.rm = TRUE)) %>%
  arrange(desc(precipitacion_media)) %>%
  slice_head(n = 10) # Selecciona las 10 primeras

# Graficar el top 10 de provincias con mayor precipitación media
ggplot(top_precipitacion, aes(x = reorder(provincia, precipitacion_media), y = precipitacion_media)) +
  geom_bar(stat = "identity", fill = "lightgreen") +
  coord_flip() +
  labs(title = "Top 10 Provincias con Mayor Precipitación Media",
       x = "Provincia", y = "Precipitación Media (mm)") +
  theme_minimal()

Misiones lidera el ranking de las provincias argentinas con mayor precipitación media, alcanzando aproximadamente 4.5 mm. Se observa un patrón interesante donde las provincias del noreste argentino (Misiones, Corrientes, Chaco) ocupan los primeros lugares, lo cual es consistente con su clima subtropical húmedo.

Distribución de temperatura, humedad y precipitaciones por provincia

ggplot(df_completo, aes(x = fct_reorder(provincia, temperatura, .fun = median), y = temperatura)) +
  geom_boxplot() +
  labs(title = "Distribución de Temperatura por Provincia (Ordenado)",
       x = "Provincia (Ordenado por Mediana de Temperatura)",
       y = "Temperatura (°C)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
Warning: `fct_reorder()` removing 1223 missing values.
ℹ Use `.na_rm = TRUE` to silence this message.
ℹ Use `.na_rm = FALSE` to preserve NAs.
Warning: Removed 1223 rows containing non-finite outside the scale range
(`stat_boxplot()`).

ggplot(df_completo, aes(x = fct_reorder(provincia, humedad, .fun = median), y = humedad)) +
  geom_boxplot() +
  labs(title = "Distribución de Humedad por Provincia (Ordenado)",
       x = "Provincia (Ordenado por Mediana de Humedad)",
       y = "Humedad (%)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
Warning: `fct_reorder()` removing 3762 missing values.
ℹ Use `.na_rm = TRUE` to silence this message.
ℹ Use `.na_rm = FALSE` to preserve NAs.
Warning: Removed 3762 rows containing non-finite outside the scale range
(`stat_boxplot()`).

ggplot(df_completo, aes(x = fct_reorder(provincia, precipitacion, .fun = median), y = precipitacion)) +
  geom_boxplot() +
  labs(title = "Distribución de Precipitación por Provincia (Ordenado)",
       x = "Provincia (Ordenado por Mediana de Precipitación)",
       y = "Precipitación (mm)") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))
Warning: `fct_reorder()` removing 10699 missing values.
ℹ Use `.na_rm = TRUE` to silence this message.
ℹ Use `.na_rm = FALSE` to preserve NAs.
Warning: Removed 10699 rows containing non-finite outside the scale range
(`stat_boxplot()`).

Buscamos hacer un raster en este caso de precipitaciones por provincias pero nos va a servir para la temperatura y demas variables

precipitaciones_por_provincia <- df_completo %>%
  group_by(provincia) %>%
  summarise(
    precipitacion_promedio = mean(precipitacion, na.rm = TRUE),
    precipitacion_total = sum(precipitacion, na.rm = TRUE),
    num_estaciones = n_distinct(id_estacion)
  )



mapa_precipitaciones <- argentina %>%
  left_join(precipitaciones_por_provincia, 
            by = c("provincia" = "provincia"))
# Definir una paleta de colores fríos
p3 <- ggplot(mapa_precipitaciones) +
  geom_sf(aes(fill = precipitacion_promedio)) +
  scale_fill_gradientn(
    name = "Precipitación Promedio (mm)", 
    colours = c("#F0F8FF", "#B0E0E6", "#4682B4", "#00008B"),
    na.value = "grey50"
  ) +
  labs(
    title = "Precipitaciones Promedio por Provincia",
    subtitle = "Datos del Servicio Meteorológico Nacional"
  ) +
  theme_minimal() +
  theme(
    axis.text = element_blank(),
    axis.title = element_blank()
  ) +
  annotation_scale() +
  annotation_north_arrow()

p3

El mapa refleja claramente cómo las provincias del noreste, como Misiones y Corrientes, tienen las precipitaciones promedio más altas, mientras que las regiones del oeste y sur, como San Juan y Santa Cruz, muestran precipitaciones significativamente menores. La incorporación de elementos como la escala y la flecha de norte ayuda a contextualizar geográficamente los datos y facilita la lectura del mapa. Este tipo de visualización es útil para analizar patrones espaciales de precipitación y para comparaciones visuales de la humedad entre diferentes provincias en Argentina.

Integramos shiny para poder tener una mayor utilidad y comodidad a la hora de ver datos

# Aplicación Shiny
ui <- fluidPage(
  titlePanel("Temperatura Media en Argentina por Provincia"),
  
  sidebarLayout(
    sidebarPanel(
      selectInput("provincia", "Seleccionar Provincia", choices = unique(df_completo$provincia)),
      selectInput("anio", "Seleccionar Año", choices = unique(df_completo$año)),
      selectInput("mes", "Seleccionar Mes", choices = 1:12, selected = 1),
      actionButton("update", "Actualizar")
    ),
    
    mainPanel(
      leafletOutput("mapa")
    )
  )
)

server <- function(input, output, session) {
  
  # Filtrar y calcular temperatura media de la provincia seleccionada
  datos_provincia <- reactive({
    req(input$update)
    df_completo %>%
      filter(provincia == input$provincia, año == input$anio, mes == input$mes) %>%
      summarize(temp_media_provincia = mean(temperatura, na.rm = TRUE)) %>%
      pull(temp_media_provincia)
  })
  
  # Renderizar el mapa interactivo
  output$mapa <- renderLeaflet({
    
    # Obtener la temperatura media de la provincia seleccionada
    temp_media <- datos_provincia()
    
    # Crear el mapa
    leaflet(argentina) %>%
      addTiles() %>%
      
      # Colorear la provincia seleccionada según su temperatura media y mostrar tooltip
      addPolygons(data = argentina[argentina$provincia == input$provincia, ],
                  fillColor = colorNumeric("YlOrRd", domain = NULL)(temp_media),
                  color = "black", weight = 1, fillOpacity = 0.7,
                  label = ~paste("Provincia:", input$provincia, "<br>",
                                 "Temperatura Media:", round(temp_media, 2), "°C"),
                  labelOptions = labelOptions(direction = "auto", textsize = "15px")) %>%
      
      # Colocar marcadores de las estaciones meteorológicas y mostrar su temperatura media
      addCircleMarkers(data = df_completo %>%
                         filter(provincia == input$provincia, año == input$anio, mes == input$mes) %>%
                         group_by(id_estacion, latitud, longitud) %>%
                         summarize(temp_media = mean(temperatura, na.rm = TRUE)),
                       lng = ~longitud, lat = ~latitud,
                       radius = 5,
                       color = ~colorNumeric("YlOrRd", domain = NULL)(temp_media),
                       popup = ~paste("Estación ID:", id_estacion, "<br>",
                                      "Temperatura Media:", round(temp_media, 2), "°C")) %>%
      
      # Agregar leyenda de temperatura media
      addLegend("bottomright", 
                pal = colorNumeric("YlOrRd", domain = NULL),
                values = c(min(df_completo$temperatura, na.rm = TRUE), max(df_completo$temperatura, na.rm = TRUE)),
                title = "Temperatura Media (°C)")
  })
}

shinyApp(ui = ui, server = server)

Shiny applications not supported in static R Markdown documents

Trabajamos más a profundidad sobre las variables temperatura y precipitaciones

En los siguientes graficos buscamos analizar estas variables por separado, buscando tendencias o anomalías

temp_provincia <- df_completo %>%
    group_by(provincia) %>%
    summarise(Temperatura_Media = mean(temperatura, na.rm = TRUE))


ggplot(temp_provincia, aes(x = reorder(provincia, Temperatura_Media), y = Temperatura_Media)) +
    geom_bar(stat = "identity", fill = "skyblue") +
    coord_flip() +  
    labs(title = "Temperatura Media por Provincia", x = "Provincia", y = "Temperatura Media (°C)") +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))

temp_mensual <- df_completo %>%
    group_by(mes) %>%
    summarise(Temperatura_Media = mean(temperatura, na.rm = TRUE))


ggplot(temp_mensual, aes(x = mes, y = Temperatura_Media)) +
    geom_line(color = "darkorange", size = 1) +
    geom_point(color = "darkred", size = 2) +
    scale_x_continuous(breaks = 1:12, labels = c("Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", 
                                                 "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre")) +
    labs(title = "Temperatura Media a lo Largo del Año", x = "Mes", y = "Temperatura Media (°C)") +
    theme_minimal()

Nada fuera de lo esperado, chequeamos que que el comportamiento sea el correcto.

precip_provincia <- df_completo %>%
    group_by(provincia) %>%
    summarise(Precipitacion_Media = mean(precipitacion, na.rm = TRUE))


ggplot(precip_provincia, aes(x = reorder(provincia, Precipitacion_Media), y = Precipitacion_Media)) +
    geom_bar(stat = "identity", fill = "lightblue") +
    coord_flip() +  
    labs(title = "Precipitación Media por Provincia", x = "Provincia", y = "Precipitación Media (mm)") +
    theme_minimal() +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))

df_temp <- df_completo %>%
  mutate(DateTime = as.POSIXct(paste(fecha, hora), format="%Y-%m-%d %H"))

#Calcular la temperatura promedio mensual por provincia
f_monthly_province <- df_temp %>%
  mutate(month = month(DateTime, label = TRUE, abbr = TRUE)) %>%
  group_by(provincia, month) %>%
  summarise(monthly_temp_avg = mean(temperatura, na.rm = TRUE), .groups = 'drop')

#Crear el gráfico con cada línea representando una provincia
p <- ggplot(f_monthly_province, aes(x = month, y = monthly_temp_avg, color = factor(provincia), group = provincia)) +
  geom_line(size = 1) +
  geom_point(size = 2) +
  labs(title = "Temperatura Promedio Mensual por Provincia",
       x = "Mes", y = "Temperatura Promedio (°C)", color = "Provincia") +
  theme_minimal() +
  theme(legend.position = "bottom")

#Convertir el gráfico a interactivo con plotly
p_interactive <- ggplotly(p) %>%
  layout(legend = list(title = list(text = "<b>Provincia</b>")))

p_interactive

Temperatura media del Mes por año

f_monthly_year <- df_temp%>%
  mutate(year = year(DateTime),
         month = month(DateTime, label = TRUE, abbr = TRUE)) %>%
  group_by(year, month) %>%
  summarise(monthly_temp_avg = mean(temperatura, na.rm = TRUE), .groups = 'drop')

p <- ggplot(f_monthly_year, aes(x = month, y = monthly_temp_avg, color = factor(year), group = year)) +
  geom_line(size = 1) +
  geom_point(size = 2) +
  labs(title = "Temperatura Promedio Mensual en Distintos Años",
       x = "Mes", y = "Temperatura Promedio (°C)", color = "Año") +
  theme_minimal() +
  theme(legend.position = "bottom")

#Convertir el gráfico a interactivo con plotly
p_interactive <- ggplotly(p) %>%
  layout(legend = list(title = list(text = "<b>Año</b>")))

p_interactive

Este gráfico muestra la variación de la temperatura promedio mensual en Argentina para los años 2018 a 2024. Cada línea de color representa un año distinto, lo que permite comparar el patrón estacional y las fluctuaciones de temperatura entre estos años. Se observa una tendencia común: las temperaturas alcanzan su punto más alto en el verano (enero a marzo) y descienden a sus valores más bajos en invierno (junio a agosto). Las líneas tienden a agruparse de manera similar en los meses correspondientes, pero hay ligeras diferencias en algunos meses, lo que indica variaciones interanuales en el clima.

library(ggplot2)
library(dplyr)
library(ggmap)  # Para mapas
library(lubridate)  # Para manipular fechas

# Suponiendo que df_completo ya está cargado en tu entorno

# 1. Gráfico de línea de temperatura a lo largo de los años
grafico_temp <- df_completo %>%
  group_by(fecha) %>%
  summarise(media_temp = mean(temperatura, na.rm = TRUE)) %>%
  ggplot(aes(x = fecha, y = media_temp)) +
  geom_line(color = "blue") +
  labs(title = "Temperatura Media Diaria en Argentina (2018-2023)",
       x = "Fecha", y = "Temperatura (°C)") +
  theme_minimal()

# Convertir el gráfico a interactivo con plotly
grafico_temp_interactivo <- ggplotly(grafico_temp)
grafico_temp_interactivo

Este gráfico de línea muestra la temperatura media diaria en Argentina desde 2018 hasta 2023, permitiendo visualizar variaciones diarias y patrones a lo largo de los años. Los picos regulares indican los períodos de verano (con temperaturas más altas), mientras que los valles corresponden al invierno (temperaturas más bajas). La gráfica muestra una notable estacionalidad, donde se repiten los ciclos de temperatura anuales. Además, permite observar picos atípicos y días específicos en los que las temperaturas fueron particularmente altas o bajas

estacionalidad <- df_completo %>%
  group_by(mes) %>%
  summarize(temp_promedio = mean(temperatura, na.rm = TRUE),
            precip_promedio = mean(precipitacion, na.rm = TRUE))

# Crear el gráfico en ggplot2
grafico_estacional <- ggplot(estacionalidad, aes(x = mes)) +
  geom_line(aes(y = temp_promedio, color = "Temperatura Promedio (°C)")) +
  geom_line(aes(y = precip_promedio * 10, color = "Precipitación Promedio (mm)")) + # Escalado para visualizar
  scale_y_continuous(sec.axis = sec_axis(~./10, name = "Precipitación Promedio (mm)")) +
  labs(title = "Patrones estacionales de Temperatura y Precipitación",
       x = "Mes", y = "Temperatura (°C) / Precipitación (mm)") +
  scale_color_manual(name = "Variable", values = c("Temperatura Promedio (°C)" = "red", "Precipitación Promedio (mm)" = "blue")) +
  theme_minimal()

# Convertir el gráfico a interactivo con plotly
grafico_estacional_interactivo <- ggplotly(grafico_estacional)
grafico_estacional_interactivo

como vemos cierta estacionalidad, y esta correcto, decidimos hacer un grafico de temperatura promedio por hora para las distintas estaciones del año

# Crear el dataset con la estructura deseada a partir de df_completo

# Crear el dataset con la estructura deseada a partir de df_completo
df_promedio_temp_estacion <- df_completo %>%
  mutate(
    Hora = df_completo$hora,
    Estacion = case_when(
      month(fecha) %in% c(9, 10, 11) ~ "Primavera",
      month(fecha) %in% c(12, 1, 2) ~ "Verano",
      month(fecha) %in% c(3, 4, 5) ~ "Otoño",
      month(fecha) %in% c(6, 7, 8) ~ "Invierno"
    )
  ) %>%
  group_by(Hora, Estacion) %>%
  summarize(temperatura_promedio = mean(temperatura, na.rm = TRUE)) %>%
  ungroup()
`summarise()` has grouped output by 'Hora'. You can override using the
`.groups` argument.
# Colores para cada estación
colores_estaciones <- c("Primavera" = "coral", "Verano" = "yellow", "Otoño" = "plum", "Invierno" = "lightblue")

# Interfaz de usuario en Shiny
ui <- fluidPage(
  titlePanel("Comparación Interactiva de Temperatura Promedio por Hora entre Estaciones"),
  sidebarLayout(
    sidebarPanel(
      checkboxGroupInput("estacion", "Seleccione estaciones para mostrar:",
                         choices = c("Verano", "Otoño", "Primavera", "Invierno"),
                         selected = c("Verano", "Otoño", "Primavera", "Invierno")) 
    ),
    mainPanel(
      plotlyOutput("tempPlot")  # Usamos plotlyOutput en lugar de plotOutput
    )
  )
)

# Lógica del servidor en Shiny
server <- function(input, output) {
  output$tempPlot <- renderPlotly({
    
    # Filtrar datos según las estaciones seleccionadas
    df_filtrado <- df_promedio_temp_estacion %>% filter(Estacion %in% input$estacion)

    # Generar gráfico de líneas
    p <- ggplot(df_filtrado, aes(x = Hora, y = temperatura_promedio, color = Estacion, group = Estacion)) +
      geom_line(size = 1.2) +
      labs(
        title = "Temperatura Promedio por Hora en Diferentes Estaciones",
        x = "Hora del Día",
        y = "Temperatura Promedio (°C)",
        color = "Estación"
      ) +
      scale_color_manual(values = colores_estaciones) +
      scale_x_continuous(breaks = 0:23) +
      theme_minimal() +
      theme(
        legend.position = "bottom"
      )
    
    # Convertir el gráfico ggplot a un gráfico interactivo con plotly
    ggplotly(p) %>%
      layout(legend = list(orientation = "h", x = 0.5, xanchor = "center"))  # Centramos la leyenda en la parte inferior
  })
}

# Ejecutar la aplicación Shiny
shinyApp(ui = ui, server = server)

Shiny applications not supported in static R Markdown documents

El gráfico de líneas para temperatura y precipitacion por mes resalta la estacionalidad de ambas variables. Si observamos aumentos de temperatura en los meses de verano y disminuciones en invierno, así como picos de precipitación en ciertas estaciones, esto indica patrones estacionales claros. Este análisis es fundamental para entender cómo cambia el clima a lo largo del año y cómo pueden variar las condiciones climáticas en cada estación.

temp_anual <- df_completo %>%
  group_by(año) %>%
  summarize(temp_promedio = mean(temperatura, na.rm = TRUE))

# Regresión lineal sobre la temperatura promedio
modelo_temp <- lm(temp_promedio ~ año, data = temp_anual)

# Crear el gráfico en ggplot2
grafico_tendencia <- ggplot(temp_anual, aes(x = año, y = temp_promedio)) +
  geom_line(color = "blue") +
  geom_smooth(method = "lm", color = "red", se = FALSE) +
  labs(title = "Tendencia de cambio en la Temperatura Promedio Anual",
       x = "Año", y = "Temperatura Promedio (°C)") +
  theme_minimal()

# Convertir el gráfico a interactivo con plotly
grafico_tendencia_interactivo <- ggplotly(grafico_tendencia)
grafico_tendencia_interactivo

Este gráfico de líneas muestra la evolución de la temperatura promedio anual. Si se observa una tendencia ascendente, podría ser una señal de calentamiento global. Una tendencia descendente o variaciones abruptas, en cambio, podrían indicar fluctuaciones naturales o cambios en las condiciones climáticas locales.

Analisis de series temporales

intento de prediccion con prophet para la variable precipitaciones

# Convertir a tipo Date y calcular la media diaria de precipitaciones

data_diaria <- df_completo %>%
  group_by(fecha) %>%
  summarize(precipitacion = mean(precipitacion, na.rm = TRUE))

# Renombrar columnas para Prophet
data_prophet <- data_diaria %>%
  rename(ds = fecha, y = precipitacion)

# Crear y ajustar el modelo de Prophet
model <- prophet(data_prophet)

# Realizar una predicción para el futuro (por ejemplo, los próximos 365 días)
future <- make_future_dataframe(model, periods = 365)
forecast <- predict(model, future)

# Marcar los datos históricos y los datos de predicción
data_prophet$tipo <- "Histórico"
forecast$tipo <- "Predicción"

# Unir datos históricos y predicción en un solo dataframe para graficar
datos_completos <- bind_rows(data_prophet, forecast %>% dplyr::select(ds, yhat) %>% rename(y = yhat))

# Graficar los datos históricos y la predicción con distinción
p <- ggplot(datos_completos, aes(x = ds, y = y, color = tipo)) +
  geom_line() +
  labs(title = "Predicción de Precipitaciones (Media Diaria) con Prophet",
       x = "Fecha",
       y = "Precipitación (mm)",
       color = "Tipo de dato") +
  scale_color_manual(values = c("Histórico" = "blue", "Predicción" = "red")) +
  theme_minimal()

# Convertir el gráfico ggplot2 a plotly para interactividad
p_interactivo <- ggplotly(p)

# Mostrar el gráfico interactivos
p_interactivo 

La serie de tiempo muestra variaciones estacionales en las precipitaciones. Se pueden identificar períodos con aumentos significativos, probablemente reflejando la temporada de lluvias en Argentina.

El modelo proyecta las precipitaciones a futuro basándose en la estacionalidad y tendencia observada. La predicción puede mostrar un patrón recurrente de aumento y disminución en función de las temporadas húmedas y secas.

Observar cuándo y cuánto fluctúan las precipitaciones a lo largo del año es útil para anticipar la disponibilidad de agua en ciertas estaciones. Esto puede beneficiar la planificación agrícola o la gestión de recursos hídricos en áreas sensibles a cambios en el régimen de lluvias.

intento de prediccion con prophet sobre la variable temperatura

data_diaria_temp <- df_completo %>%
  group_by(fecha) %>%
  summarize(temperatura = mean(temperatura, na.rm = TRUE))

# Renombrar columnas para Prophet
data_prophet_temp <- data_diaria_temp %>%
  rename(ds = fecha, y = temperatura)

# Crear y ajustar el modelo de Prophet
model_temp <- prophet(data_prophet_temp)
Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.
# Realizar una predicción para el futuro (por ejemplo, los próximos 365 días)
future_temp <- make_future_dataframe(model, periods = 365)
forecast_temp <- predict(model, future)

# Marcar los datos históricos y los datos de predicción
data_prophet_temp$tipo <- "Histórico"
forecast_temp$tipo <- "Predicción"

# Unir datos históricos y predicción en un solo dataframe para graficar
datos_completos_temp <- bind_rows(data_prophet_temp, forecast_temp %>% dplyr::select(ds, yhat) %>% rename(y = yhat))

# Crear gráfico con ggplot2
p <- ggplot(datos_completos_temp, aes(x = ds, y = y, color = tipo)) +
  geom_line() +
  labs(title = "Predicción de Temperatura (Media Diaria) con Prophet",
       x = "Fecha",
       y = "Temperatura (°C)",
       color = "Tipo de dato") +
  scale_color_manual(values = c("Histórico" = "lightblue", "Predicción" = "orange")) +
  theme_minimal()

# Convertir el gráfico ggplot2 a plotly para interactividad
p_interactivo <- ggplotly(p)

# Mostrar el gráfico interactivo
p_interactivo

La temperatura promedio diaria presenta variaciones estacionales bien definidas, con picos en los meses de verano y caídas en invierno, lo que refleja el patrón climático anual de Argentina.

Este gráfico ayuda a visualizar los períodos de temperatura extrema, lo cual es importante para sectores como la agricultura y la salud. La predicción estacional puede informar decisiones sobre cultivos, manejo energético, o preparar servicios de salud ante eventos de calor extremo.